﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using Db4objects.Db4o.Activation;
using Db4objects.Db4o.Collections;

namespace ZOIL.DataBackend.Db4o
{
    public sealed class ObservableCollection4<T> : ObservableCollection<T>, IActivatableCollection<T>
    {
        #region Constructor

        public ObservableCollection4()
        {
            CollectionChanged += OnCollectionChanged;
            PropertyChanged += OnPropertyChanged;
        }

        #endregion

        #region Change Handler

        void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "OnRemoteUpdate")
            {
                return;
            }
            Activate(ActivationPurpose.Write);
        }

        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Activate(ActivationPurpose.Write);
        }

        #endregion

        #region List Implementation

        public new void Move(int oldIndex, int newIndex)
        {
            Activate(ActivationPurpose.Write);
            MoveItem(oldIndex, newIndex);
        }

        #endregion

        #region IList<T> Members

        public new int IndexOf(T item)
        {
            Activate(ActivationPurpose.Read);
            return base.IndexOf(item);
        }

        public new void Insert(int index, T item)
        {
            Activate(ActivationPurpose.Write);
            base.Insert(index, item);
        }

        public new void RemoveAt(int index)
        {
            Activate(ActivationPurpose.Write);
            base.RemoveAt(index);
        }

        public new T this[int index]
        {
            get
            {
                Activate(ActivationPurpose.Read);
                return base[index];
            }
            set
            {
                SetItem(index, value);
                Activate(ActivationPurpose.Write);
                base[index] = value;
            }
        }

        #endregion

        #region ICollection<T> Members

        public new void Add(T item)
        {
            Activate(ActivationPurpose.Write);
            base.Add(item);
        }

        public new void Clear()
        {
            Activate(ActivationPurpose.Write);
            base.Clear();
        }

        public new bool Contains(T item)
        {
            Activate(ActivationPurpose.Read);
            return base.Contains(item);
        }

        public new int Count
        {
            get
            {
                Activate(ActivationPurpose.Read);
                return base.Count;
            }
        }

        public new bool Remove(T item)
        {
            Activate(ActivationPurpose.Write);
            return base.Remove(item);
        }

        #endregion

        #region IEnumerable<T> Members

        public new IEnumerator<T> GetEnumerator()
        {
            Activate(ActivationPurpose.Read);
            return base.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            Activate(ActivationPurpose.Read);
            return base.GetEnumerator();
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Called by base class Collection&lt;T&gt; when the list is being cleared;
        /// raises a CollectionChanged event to any listeners.
        /// </summary> 
        private new void ClearItems()
        {
            Activate(ActivationPurpose.Write);
            base.ClearItems();
        }

        /// <summary>
        /// Called by base class Collection&lt;T&gt; when an item is removed from list; 
        /// raises a CollectionChanged event to any listeners. 
        /// </summary>
        private new void RemoveItem(int index)
        {
            Activate(ActivationPurpose.Write);
            base.RemoveItem(index);
        }

        /// <summary>
        /// Called by base class Collection&lt;T&gt; when an item is added to list; 
        /// raises a CollectionChanged event to any listeners.
        /// </summary> 
        private new void InsertItem(int index, T item)
        {
            Activate(ActivationPurpose.Write);
            base.InsertItem(index, item);
        }

        /// <summary>
        /// Called by base class Collection&lt;T&gt; when an item is set in list; 
        /// raises a CollectionChanged event to any listeners.
        /// </summary>
        private new void SetItem(int index, T item)
        {
            Activate(ActivationPurpose.Write);
            base.SetItem(index, item);
        }

        /// <summary> 
        /// Called by base class ObservableCollection&lt;T&gt; when an item is to be moved within the list;
        /// raises a CollectionChanged event to any listeners. 
        /// </summary> 
        private new void MoveItem(int oldIndex, int newIndex)
        {
            Activate(ActivationPurpose.Write);
            base.MoveItem(oldIndex, newIndex);
        }

        /// <summary>
        /// Raise CollectionChanged event to any listeners. 
        /// Properties/methods modifying this ObservableCollection will raise
        /// a collection changed event through this virtual method. 
        /// </summary> 
        /// <remarks>
        /// When overriding this method, either call its base implementation 
        /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
        /// </remarks>
        private new void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            Activate(ActivationPurpose.Read);
            base.OnCollectionChanged(e);
        }

        #endregion Protected Methods

        #region IActivatable Members

        [NonSerialized]
        private IActivator _activator;

        public void Activate(ActivationPurpose purpose)
        {
            if (_activator == null)
                return;
            _activator.Activate(purpose);
        }

        public void Bind(IActivator activator)
        {
            if (_activator == activator)
            {
                return;
            }
            if (activator != null && null != _activator)
            {
                throw new InvalidOperationException();
            }
            _activator = activator;
        }

        #endregion
    }
}
